#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dig.h"
#include "util/algorithm.h"
#include "util/direction.h"
#include "util/log.h"

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif

// Cost for a normal single step in the path.
// The granularity in the lower digits is required
// for enabling the penalty for corners.
#define PATH_STEP_COST 100

// Penalty for not digging a straight path.
#define PATH_CORNER_PENALTY 1

// This cost is added to potential tunnels that would require us to add
// a fresh layer on top of the map. Low values generate wasteful extra
// map layers and pollute them with tunnels before the rooms get placed.
// High values lead the path algorithm to accept long-winded tunnels in
// lower levels where a tunnel with stairs could save space.
#define PATH_ADD_LAYER_PENALTY 2000

// This is the cost associated with having stairs in a tunnel.
// Low values generate more confusing and disorienting tunnels.
// High values lead the path algorithm to accept long-winded tunnels in
// lower levels where a tunnel with stairs could save space.
#define PATH_STAIRS_COST 500

// Max. amount of breachable walls expected from any room. Too low
// values lead to erratic tunnel positioning. 128 is enough to deal
// with rooms 34x34 tiles in size.
#define BREACH_CANDIDATE_MAX 128

// Amount of space that a tunnel may take up when cutting room placement
// rects. min = 3. Too high values block perfectly fine unused space from
// room placement. Too low values lead to room rect fragmentation, which
// means that some room space rects may be discarded prematurely.
#define TUNNEL_CUT_WH 5

// When digging a tunnel from A to B, prohibits digging
// to levels lower than min(A.z,B.z)-TUNNEL_LAYER_LIMIT.
#define TUNNEL_LAYER_LIMIT 5

// Limit on room dimensions.
#define MAX_ROOM_WIDTH 34
#define MAX_ROOM_HEIGHT 34

// How many rects in a RectList to look at when searching
// for a suitable room container. Since the rects are
// ordered by area, the topmost N rects are also the largest
// ones. Increasing this value can quickly slow down the
// generator.
#define N_CONTAINER_RECTS 4

static RectList* tilerect = NULL;
static Layout* layout = NULL;

void resize_data ();
void tilerect_clear (int z);
Rect* find_room_container (int width, int height, Coord near);
Rect align_room (Rect* container, int width, int height, Coord near);
int place_room (Rect area);
int find_breach_walls (int room, Coord* candidates);
void nearest_pair (Coord* set1, int n_set1, Coord* set2, int n_set2, Coord* first, Coord* second);
void dig_path (Coord* path, int length);
void make_teleporter (Coord c1, Coord c2);
void dig_tunnel (int from, int to);

// prepares the dig module for an empty map with a single tile layer.
void dig_init (Layout* l)
{
	dig_cleanup (); // to be safe: remove previous traces
	layout = l;
	resize_data ();
	tilerect_clear (0);
}

void dig_cleanup ()
{
	free (tilerect);
	tilerect = NULL;
	layout = NULL;
}

/* Looking for coordinates near a, b, c and d for new room */
Coord median2c (Coord a, Coord b)
{
	return (Coord) { (a.x + b.x)/2, (a.y + b.y)/2, (a.z + b.z)/2 };
}

Coord median3c (Coord a, Coord b, Coord c)
{
	#define MEDIAN3(A,B,C) (A<B?A<C?B>C?C:B:A:A>C?B<C?C:B:A)
	return (Coord) {
		MEDIAN3(a.x,b.x,c.x),
		MEDIAN3(a.y,b.y,c.y),
		MEDIAN3(a.z,b.z,c.z)
	};
	#undef MEDIAN3
}

Coord median4c (Coord a, Coord b, Coord c, Coord d)
{
	#define MEDIAN4(A,B,C,D) ( \
		A<B?B<C?C<D?(B+C)/2:A<D?(B+D)/2:(A+B)/2: \
		B<D?A<C?(B+C)/2:(A+B)/2:A<C?A<D?(C+D)/2:(A+C)/2:C<D?(A+D)/2:(A+C)/2 \
		:A<C?C<D?(A+C)/2:B<D?(A+D)/2:(B+A)/2: \
		A<D?B<C?(A+C)/2:(B+A)/2:B<C?B<D?(C+D)/2:(B+C)/2:C<D?(B+D)/2:(B+C)/2 \
	)
	return (Coord) {
		MEDIAN4(a.x,b.x,c.x,d.x),
		MEDIAN4(a.y,b.y,c.y,d.y),
		MEDIAN4(a.z,b.z,c.z,d.z)
	};
	#undef MEDIAN4
}

int dig_room (int width, int height, int* near, int n_near)
{
	assert((n_near >= 0) && (n_near <= 8));
	assert(width <= MAX_ROOM_WIDTH);
	assert(height <= MAX_ROOM_HEIGHT);

	Coord mid;
	#define ROOM(I) (rect_middle(&layout->room[near[I]].area))

	if (n_near == 0) mid = (Coord) { layout->width/2, layout->height/2, 0 };
	else if (n_near == 1) mid = ROOM(0);
	else if (n_near == 2) mid = median2c(ROOM(0), ROOM(1));
	else if (n_near == 3) mid = median3c(ROOM(0), ROOM(1), ROOM(2));
	else { // TODO: better median function
		mid = median4c(ROOM(0), ROOM(1), ROOM(2), ROOM(3));
	}
	#undef ROOM

	Rect* container = find_room_container (width, height, mid);
	Rect r = align_room (container, width, height, mid);
	int room_id = place_room (r);

	for (int i = 0; i < n_near; i++) {
		dig_tunnel (near[i], room_id);
	}

	return room_id;
}

/*
 * From two sets of coordinates, this function attempts to find two
 * coords, one from each set, such that the distance between these
 * coords is smaller than that between any other such pair.
 * For heuristics, we assume that set1 and set2 give a rough outline of
 * rectangular areas which do not intersect each other.
 *
 * set1 points to the first element of the first set of coords.
 * n_set1 specifies the number of elements in the first set.
 * set2 points to the first element of the second set of coords.
 * n_set2 specifies the number of elements in the second set.
 * first points to the Coord struct where the first part of the
 *     result shall be written to. Initially, it should be set to
 *     a general approx. average of the coords in set1.
 * second points to the Coord struct where the second part of the
 *     result shall be written to. Initially, it should be set to
 *     a general approx. average of the coords in set2.
 */
void nearest_pair (Coord* set1, int n_set1, Coord* set2, int n_set2, Coord* first, Coord* second)
{
	assert((n_set1 > 0) && (n_set2 > 0));

	int pair_cost (Coord a, Coord b)
	{
		// If two pair_costs are equal, we try to prefer the one
		// that looks shortest by estimating the length of a direct
		// (diagonal) line.
		return (DISTC (a, b) << 22) - abs ((a.x - b.x) * (a.y - b.y));
	}

	Coord nearest_to (Coord* set, int n_set, Coord attract)
	{
		Coord best = set[0];
		int best_dist = pair_cost (set[0], attract);
		for (int i = 1; i < n_set; i++) {
			int new_dist = pair_cost (set[i], attract);
			if (new_dist < best_dist) {
				best = set[i];
				best_dist = new_dist;
			}
		}
		return best;
	}

	Coord first1 = nearest_to (set1, n_set1, *second);
	Coord second1 = nearest_to (set2, n_set2, first1);
	Coord second2 = nearest_to (set2, n_set2, *first);
	Coord first2 = nearest_to (set1, n_set1, second2);

	if (pair_cost(first1, second1) < pair_cost(first2, second2)) {
		*first = first1;
		*second = second1;
	} else {
		*first = first2;
		*second = second2;
	}
}

/*
 * This function will attempt to dig an empty corridor tunnel between
 * the two rooms from and to, crossing at most max_length tiles (not
 * counting the wall tiles framing the corridor). The algorithm will
 * first find a suitable wall to breach out into the non-room areas at
 * either end of the corridor to keep it short.
 */
void dig_tunnel (int from, int to)
{
	// first step: look for candidate walls out of the from- and to-areas
	Coord walls1 [BREACH_CANDIDATE_MAX];
	Coord walls2 [BREACH_CANDIDATE_MAX];
	int n_walls1 = find_breach_walls (from, walls1);
	int n_walls2 = find_breach_walls (to  , walls2);

	if (n_walls2 == -1) return; // already connected

	Rect* from_area = &layout->room[from].area;
	Rect* to_area = &layout->room[to].area;
	Coord breach_from = rect_middle(from_area);
	Coord breach_to = rect_middle(to_area);

	if ((n_walls1 > 0) && (n_walls2 > 0)) {
		nearest_pair(walls1, n_walls1, walls2, n_walls2, &breach_from, &breach_to);
		layout_add_room_exit (layout, from, breach_from);
		layout_add_room_entry (layout, to, breach_to);
	} else {
		log_error ("Couldn't dig tunnel %d -> %d, couldn't find enough \"breach walls\"\n", from, to);
		return;
	}

	bool can_go (Coord prev, Coord next)
	{
		(void) prev; // not needed

		// Out of bounds?
		if ((next.x <= 0) || (next.x >= layout->width -1)) return false;
		if ((next.y <= 0) || (next.y >= layout->height-1)) return false;
		if ((next.z <  0) || (next.z >  layout->layers  )) return false;

		// Can always dig in a theoretical fresh new layer
		if (next.z == layout->layers) return true;

		Coord north = { next.x, next.y-1, next.z };
		Coord west  = { next.x-1, next.y, next.z };
		Coord east  = { next.x+1, next.y, next.z };
		Coord south = { next.x, next.y+1, next.z };

		// Only at the target may we dig directly through a room tile.
		if (TILEC(layout,next).room) {
			if (COORDS_EQUAL(next,breach_to)) return true;
			else return false;
		}

		// Do not drill anywhere near other tunnels (floor tiles)
		if (TILEC(layout,north).floor) return false;
		if (TILEC(layout,west).floor) return false;
		if (TILEC(layout,east).floor) return false;
		if (TILEC(layout,south).floor) return false;

		// Also do not touch other rooms, except from/to coords
		bool dir_ok (Coord c)
		{
			if (TILEC(layout,c).room) {
				return COORDS_EQUAL(c, breach_from) || COORDS_EQUAL(c, breach_to);
			} else {
				return true;
			}
		}
		
		return dir_ok (north) && dir_ok (west) && dir_ok (east) && dir_ok (south);
	}

	int cost_step (Coord pprev, Coord prev, Coord next)
	{
		int cost = PATH_STEP_COST;

		if (direction_primary (pprev, prev) !=
			 direction_primary (prev, next)) {
			cost += PATH_CORNER_PENALTY;
		}
		
		if (prev.z != next.z) {
			cost += PATH_STAIRS_COST;
			
			if (next.z == layout->layers) {
				cost += PATH_ADD_LAYER_PENALTY;
			}
		}
		
		// The primary cost deciding factor are distance and layer
		// penalties. If costs are equal, preference shall be given to
		// paths with few turns and corners in them to make them more
		// visually attractive.
		return cost;
	}

	int cost_estimate (Coord coord)
	{
		return DISTC (coord, breach_to) * PATH_STEP_COST +
		       abs (coord.z - breach_to.z) * PATH_STAIRS_COST;
	}

	int min_z = MAX(0, MIN(breach_from.z, breach_to.z)-TUNNEL_LAYER_LIMIT);
	Space space = { 0, layout->width, 0, layout->height, min_z, layout->layers+1 };
	AstarResult result;
	astar_search (breach_from, breach_to, space, &result,
	              &can_go, &cost_step, &cost_estimate, NULL);
	
	if (result.found) {
		dig_path (result.path, result.length);
	} else {
		make_teleporter (breach_from, breach_to);
	}
}

void resize_data ()
{
	tilerect = realloc (tilerect, 
		layout->width * layout->height * layout->layers * sizeof (RectList));
}

void tilerect_clear (int z)
{
	assert((z >= 0) && (z < layout->layers));
	RectList* list = tilerect + z * layout->width * layout->height;
	for (int y = 0; y < layout->height; y++)
	for (int x = 0; x < layout->width; x++) {
		list->rect[0] = (Rect) { 0, 0, layout->width, layout->height, z };
		list->n_rects = 1;
		list++;
	}
}

Rect* find_room_container (int width, int height, Coord near)
{
	// Success must be possible in theory
	assert(width <= layout->width);
	assert(height <= layout->height);

	Rect* container = NULL;

	bool can_go (Coord from, Coord to)
	{
		if ((from.x == to.x) || (from.y == to.y)) {
			return 1;
		} else {
			return 0; // no diagonal steps
		}
	}

	bool visit_handler (Coord coord)
	{
		int index = TILEC_INDEX (layout, coord);
		assert(index >= 0);

		// if we are not in a room, go looking for rects
		for (int i = 0; i < MIN(tilerect[index].n_rects,N_CONTAINER_RECTS); i++) {
			container = &tilerect[index].rect[i];
			if (((container->right - container->left) >= width) &&
			    ((container->bottom - container->top) >= height))
			{
				return true;
			}
		}
		
		return false;
	}

	Space space = (Space) { 0, layout->width, 0, layout->height, near.z, layout->layers };
	bool found = flood_search (near, space, width/2, height/2, &can_go, &visit_handler);

	if (found) {
		return container;
	} else {
		// nothing found, get another layer and try again.
		// This time success is guaranteed (fresh layer).
		layout_extend (layout, layout->layers+1);
		resize_data ();
		tilerect_clear (layout->layers-1);
		
		int container_tile = TILE_INDEX (layout, near.x, near.y, layout->layers-1);
		return &tilerect[container_tile].rect[0];
	}
}

Rect align_room (Rect* container, int width, int height, Coord near)
{
	int room_center_min_x = container->left + width/2;
	int room_center_max_x = container->right - width/2 - width%2;
	int room_center_min_y = container->top + height/2;
	int room_center_max_y = container->bottom - height/2 - height%2;
	int room_center_x = MIN ( MAX (room_center_min_x, near.x), room_center_max_x );
	int room_center_y = MIN ( MAX (room_center_min_y, near.y), room_center_max_y );
	return (Rect) { room_center_x - width/2, room_center_y - height/2,
		room_center_x + width/2 + width%2, room_center_y + height/2 + height%2, container->z };
}

int place_room (Rect area)
{
	assert((area.left >= 0) && (area.right <= layout->width));
	assert((area.top >= 0) && (area.bottom <= layout->height));
	assert(area.z < layout->layers);

	for (int y = area.top ; y < area.bottom; y++)
	for (int x = area.left; x < area.right ; x++) {
		int index = TILE_INDEX (layout, x, y, area.z);
		tilerect[index].n_rects = 0; // no more tilerects here
	}

	int room_id = layout_add_room (layout, &area);

	// Leave a 1-tile thick wall around the new room
	Rect room_floor = (Rect) { area.left+1, area.top+1,
	                           area.right-1, area.bottom-1, area.z };
	layout_floor_rect (layout, &room_floor);

	// Modify area for cutting into tilerects everywhere.
	// We enlarge it so there will be lots of space for corridors
	// all around.
	area.left   = MAX (area.left   - 3, 0);
	area.right  = MIN (area.right  + 3, layout->width);
	area.top    = MAX (area.top    - 3, 0);
	area.bottom = MIN (area.bottom + 3, layout->height);

	for (int y = 0; y < layout->height; y++)
	for (int x = 0; x < layout->width; x++) {
		int i = TILE_INDEX(layout, x, y, area.z);
		cut_all_rects (&tilerect[i], &area, x, y);
	}

	return room_id;
}

/*
 * Evaluates the walls of the specified room to find "breachable" ones,
 * where a tunnel out of this room and into another one may be dug.
 * room is the room handle (from the layout) of the room where we are
 *     searching for breachable walls.
 * candidates is the buffer where the result coordinates are written
 *     to. It should be at least BREACH_CANDIDATE_MAX elements in size.
 * returns the number of breachable walls we identified.
 */
int find_breach_walls (int room, Coord* candidates)
{
	Rect area = layout->room[room].area;
	int insert = 0;

	assert((area.right-area.left + area.bottom-area.top - 4)*2 <= BREACH_CANDIDATE_MAX);

	/*
	 * In a rectangular room, c are the possible
	 * candidate walls we shall consider:
	 * #ccccccccccccc#
	 * c             c
	 * c             c
	 * c             c
	 * #ccccccccccccc#
	 *
	 * Candidate walls must match a certain pattern:
	 * ? - ?
	 * - c -
	 * ? . ?
	 * where - is wall, . is floor, c is the candidate and ? is
	 * anything. In this example, digging to the north is possible.
	 *
	 * Applying the pattern ensures the following goals:
	 *   - a candidate wall must be reachable from inside the room
	 *   - this exit is separated from any other exits in the room
	 *   - it is likely that we have room for a tunnel here
	 *   - at the very least, we can place a teleporter on the
	 *     candidate coords without breaching into another room or
	 *     tunnel.
	 */
	Coord outside, candidate, inside, side1, side2;
	#define IS_MATCH (( TILEC(layout,inside).floor) && \
	                  (!TILEC(layout,candidate).floor) && \
	                  (!TILEC(layout,outside).floor) && \
	                  (!TILEC(layout,side1).floor) && \
	                  (!TILEC(layout,side2).floor))

	for (int x = area.left+1; x < area.right-1; x++) {
		if (area.top > 0) {
			outside   = (Coord) { x  , area.top-1, area.z };
			candidate = (Coord) { x  , area.top  , area.z };
			inside    = (Coord) { x  , area.top+1, area.z };
			side1     = (Coord) { x-1, area.top  , area.z };
			side2     = (Coord) { x+1, area.top  , area.z };

			if (IS_MATCH) candidates[insert++] = candidate;
			if (insert >= BREACH_CANDIDATE_MAX) return insert;
		}

		if (area.bottom < layout->height) {
			outside   = (Coord) { x  , area.bottom  , area.z };
			candidate = (Coord) { x  , area.bottom-1, area.z };
			inside    = (Coord) { x  , area.bottom-2, area.z };
			side1     = (Coord) { x-1, area.bottom-1, area.z };
			side2     = (Coord) { x+1, area.bottom-1, area.z };

			if (IS_MATCH) candidates[insert++] = candidate;
			if (insert >= BREACH_CANDIDATE_MAX) return insert;
		}
	}

	for (int y = area.top+1; y < area.bottom-1; y++) {
		if (area.left > 0) {
			outside   = (Coord) { area.left-1, y  , area.z };
			candidate = (Coord) { area.left  , y  , area.z };
			inside    = (Coord) { area.left+1, y  , area.z };
			side1     = (Coord) { area.left  , y-1, area.z };
			side2     = (Coord) { area.left  , y+1, area.z };

			if (IS_MATCH) candidates[insert++] = candidate;
			if (insert >= BREACH_CANDIDATE_MAX) return insert;
		}

		if (area.right < layout->width) {
			outside   = (Coord) { area.right  , y  , area.z };
			candidate = (Coord) { area.right-1, y  , area.z };
			inside    = (Coord) { area.right-2, y  , area.z };
			side1     = (Coord) { area.right-1, y-1, area.z };
			side2     = (Coord) { area.right-1, y+1, area.z };

			if (IS_MATCH) candidates[insert++] = candidate;
			if (insert >= BREACH_CANDIDATE_MAX) return insert;
		}
	}

	#undef IS_MATCH

	return insert;
}

void dig_path (Coord* path, int length)
{
	assert(path != NULL);
	assert(length > 1);

	Coord last = path[0];
	Rect dig_rect = { path[0].x - 1, path[0].y - 1,
	                  path[0].x + 2, path[0].y + 2, last.z };
	Coord stairs = { 0, 0, 0 };
	int min_z = path[0].z; // min/max recorded layer # on path
	int max_z = path[0].z;

	inline int can_extend_rect (Coord c)
	{
		if (c.z != dig_rect.z) return 0;
		int predict_width = MAX(dig_rect.right, c.x) -
		                    MIN(dig_rect.left,  c.x);
		int predict_height = MAX(dig_rect.bottom, c.y) -
		                     MIN(dig_rect.top,    c.y);
		return (predict_width  <= TUNNEL_CUT_WH) ||
		       (predict_height <= TUNNEL_CUT_WH);
	}
	inline void extend_rect (Coord c)
	{
		dig_rect.left = MIN(dig_rect.left, c.x-1);
		dig_rect.top = MIN(dig_rect.top, c.y-1);
		dig_rect.right = MAX(dig_rect.right, c.x+2);
		dig_rect.bottom = MAX(dig_rect.bottom, c.y+2);
	}

	// special case: first path tile, no last, no rect extending
	TILEC(layout,path[0]).floor = true;

	for (int i = 1; i < length; i++) {
		Coord c = path[i];
		min_z = MIN(min_z, c.z);
		max_z = MAX(max_z, c.z);

		if (c.z >= layout->layers) {
			layout_extend (layout, c.z+1);
			resize_data ();
			tilerect_clear (c.z);
		}

		TILEC(layout,c).floor = true;

		if (can_extend_rect(c)) {
			extend_rect (c);
		} else {
			// cut room rects so we don't plant rooms on top of this tunnel later on
			for (int y = 0; y < layout->height; y++)
			for (int x = 0; x < layout->width ; x++) {
				int ti = TILE_INDEX(layout, x, y, dig_rect.z);
				cut_all_rects (&tilerect[ti], &dig_rect, x, y);
			}
			dig_rect = (Rect) { c.x-1, c.y-1, c.x+2, c.y+2, c.z };
		}

		// plant stairs on the way
		if ((c.z != last.z) && (stairs.x == 0) && (stairs.y == 0)) {
			stairs = last;
		} else
		if ((c.z == last.z) && (stairs.x == last.x) && (stairs.y == last.y)) {
			Coord anchor[2] = { stairs, last };
			layout_add_traversal (layout, TRAVERSAL_STAIRS, anchor);
			stairs = (Coord) { 0, 0, 0 };
		}

		last = c;
	}

	// tie up loose ends
	if (!((stairs.x == 0) && (stairs.y == 0))) {
		Coord anchor[2] = { stairs, last };
		layout_add_traversal (layout, TRAVERSAL_STAIRS, anchor);
	}
}

void make_teleporter (Coord c1, Coord c2)
{
	Rect c1rect = { c1.x, c1.y, c1.x+1, c1.y+1, c1.z };
	Rect c2rect = { c2.x, c2.y, c2.x+1, c2.y+1, c2.z };
	layout_floor_rect (layout, &c1rect);
	layout_floor_rect (layout, &c2rect);
	Coord anchor[2] = { c1, c2 };
	layout_add_traversal (layout, TRAVERSAL_TELEPORTER, anchor);
}

#ifdef TEST_DIG
#include <stdio.h>

void print_layout (Layout* layout, int tiles, int traversals, int rooms, const char* filename)
{
	FILE* out = stdout;
	if (filename != NULL) out = fopen(filename, "w");
	assert(out);

	if (tiles) {
		fprintf (out, "Tile Layout *************************\n");
		for (int z = 0; z < layout->layers; z++) {
			fprintf (out, "-- Layer %d --\n", z);
			for (int y = 0; y < layout->height; y++) {
				for (int x = 0; x < layout->width; x++) {
					char c = ' ';
					int wall = !TILE(layout,x,y,z).floor;
					int room = TILE(layout,x,y,z).room;
					if (wall) c = 'X';
					if (wall && room) c = '=';
					fprintf (out, "%c ", c);
				}
				fprintf (out, "\n");
			}
			fprintf (out, "\n");
		}
	}

	if (traversals) {
		fprintf (out, "Traversals **************************\n");
		for (int i = 0; i < layout->n_traversals; i++) {
			Traversal* t = &layout->traversal[i];
			fprintf (out, "%s from (%d,%d,%d) to (%d,%d,%d)\n",
			        t->type == TRAVERSAL_STAIRS ? "Stairs" : "Teleporter",
			        t->anchor[0].x, t->anchor[0].y, t->anchor[0].z,
			        t->anchor[1].x, t->anchor[1].y, t->anchor[1].z);
		}
	}

	if (rooms) {
		fprintf (out, "Rooms *******************************\n");
		for (int i = 0; i < layout->n_rooms; i++) {
			Rect* r = &layout->room[i];
			fprintf (out, "On layer %d from (%d,%d) to (%d,%d)\n",
			        r->z, r->left, r->top, r->right, r->bottom);
		}
	}

	if (filename != NULL) fclose(out);
}

int main ()
{
	Layout layout;
	algorithm_init (100000);
	
	// -----------------------------------------
	
	layout_init (&layout, 30, 16, 1, 1000, 1000);
	dig_init (&layout);

	int room1 = place_room ((Rect) { 2, 2, 7, 7, 0 });
	int room2 = place_room ((Rect) { 12, 2, 17, 7, 0 });
	
	fprintf (stderr, "rooms %d %d\n", room1, room2);
	
	dig_tunnel (room1, room2);
	
	dig_cleanup ();
	print_layout (&layout, 1, 1, 1, NULL);
	layout_cleanup (&layout);
	
	// -----------------------------------------
	
	layout_init (&layout, 35, 30, 1, 1000, 1000);
	dig_init (&layout);
	
	int prev [4];
	prev[0] = dig_room (8, 15, prev, 0);
	prev[2] = dig_room (9, 7, prev, 1);
	prev[1] = dig_room (10, 5, prev, 1 );
	prev[0] = prev[2];
	prev[3] = dig_room (20, 14, prev, 2 );
	prev[0] = prev[3];
	prev[0] = dig_room (7, 13, prev, 1 );
	prev[1] = dig_room (9, 15, prev, 1 );
	dig_room (10, 10, prev, 4 );

	dig_cleanup ();
	print_layout (&layout, 1, 1, 1, NULL);
	layout_cleanup (&layout);
	
	// -----------------------------------------
	
	return 0;
}
#endif /* TEST_DIG */
